/*
 * $Id: SortController.java 3498 2009-09-10 09:35:40Z kleopatra $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.jdesktop.swingx.sort;

import java.util.Comparator;

import javax.swing.RowFilter;
import javax.swing.SortOrder;

/**
 * Defines the interactive sort control for sortable collection components (like
 * JXList, JXTable). All sort gesture requests from their sort api are routed
 * through the SortController.
 * <p>
 * 
 * This is very-much work-in-progress: while moving from ol' SwingX sorting to
 * core jdk6 sorting we need a hook for sorting api on the view. So in terms of
 * jdk6 classes, this is something like:
 * <p>
 * 
 * <code><pre>
 * SortController == DefaultRowSorter - RowSorter + XX
 * </pre></code> All methods which change sort state must respect per-controller
 * and per-column sortable property, as follows
 * <ol>
 * <li>if per-controller sortable is false, do nothing
 * <li>if per-controller sortable is true, if per-column sortable is false, do
 * nothing
 * <li>if both are true toggle the SortOrder of the given column
 * </ol>
 *
 * 
 * @author <a href="mailto:jesse@swank.ca">Jesse Wilson</a>
 * @author Jeanette Winzenburg
 * 
 */
public interface SortController<M> {

	// ----------------- configuration

	/**
	 * Sets whether or not this controller is sortable.
	 * <p>
	 * 
	 * The default is true.
	 * <p>
	 * 
	 * PENDING JW: define behaviour if sortable is disabled while has
	 * sortOrders. In this case JXTable resets all sorts.
	 * 
	 * @param sortable
	 *            whether or not this controller is sortable
	 * @see #isSortable()
	 */
	void setSortable(boolean sortable);

	/**
	 * Returns true if this controller is sortable; otherwise, false.
	 *
	 * @return true if this controller is sortable
	 * 
	 * @see #isSortable()
	 */
	boolean isSortable();

	/**
	 * Sets whether or not the specified column is sortable.
	 * <p>
	 * 
	 * The default is true.
	 * <p>
	 * 
	 * PENDING JW: define behaviour if sortable is disabled while has
	 * sortOrders. In this case JXTable removes the sort of the column.
	 * <p>
	 * 
	 * PENDING JW: decide whether or not this method should trigger a resort
	 * DefaultRowSorter explicitly doesn't, JXTable does.
	 * 
	 * @param column
	 *            the column to enable or disable sorting on, in terms of the
	 *            underlying model
	 * @param sortable
	 *            whether or not the specified column is sortable
	 * @throws IndexOutOfBoundsException
	 *             if <code>column</code> is outside the range of the model
	 * @see #isSortable(int)
	 * @see #toggleSortOrder(int)
	 * @see #setSortOrder(int, SortOrder)
	 */
	void setSortable(int column, boolean sortable);

	/**
	 * Returns true if the specified column is sortable.
	 * <p>
	 * This returns true if both the controller's sortable property and the
	 * column's sortable property is true. Returns false if any of them is
	 * false.
	 *
	 * @param column
	 *            the column to check sorting for, in terms of the underlying
	 *            model
	 * @return true if the column is sortable
	 * @throws IndexOutOfBoundsException
	 *             if column is outside the range of the underlying model
	 * 
	 * @see #isSortable(int)
	 */
	boolean isSortable(int column);

	/**
	 * Sets the <code>Comparator</code> to use when sorting the specified
	 * column. This does not trigger a sort. If you want to sort after setting
	 * the comparator you need to explicitly invoke <code>sort</code>.
	 *
	 * @param column
	 *            the index of the column the <code>Comparator</code> is to be
	 *            used for, in terms of the underlying model
	 * @param comparator
	 *            the <code>Comparator</code> to use
	 * @throws IndexOutOfBoundsException
	 *             if <code>column</code> is outside the range of the underlying
	 *             model
	 */
	public void setComparator(int column, Comparator<?> comparator);

	/**
	 * Returns the <code>Comparator</code> for the specified column. This will
	 * return <code>null</code> if a <code>Comparator</code> has not been
	 * specified for the column.
	 *
	 * @param column
	 *            the column to fetch the <code>Comparator</code> for, in terms
	 *            of the underlying model
	 * @return the <code>Comparator</code> for the specified column
	 * @throws IndexOutOfBoundsException
	 *             if column is outside the range of the underlying model
	 */
	public Comparator<?> getComparator(int column);

	/**
	 * Sets the cycle of sort ordes to toggle through. Zero or more SortOrders
	 * which must not be null.
	 * 
	 * @param cycle
	 *            the SortOrders to cycle through, may be empty
	 * @throws NullPointerException
	 *             if the array or any of its elements is null
	 */
	void setSortOrderCycle(SortOrder... cycle);

	/**
	 * Returns the cycle of sort orders to cycle through.
	 * 
	 * @return
	 */
	SortOrder[] getSortOrderCycle();

	/**
	 * If true, specifies that a sort should happen when the underlying model is
	 * updated (<code>rowsUpdated</code> is invoked). For example, if this is
	 * true and the user edits an entry the location of that item in the view
	 * may change. The default is true.
	 *
	 * @param sortsOnUpdates
	 *            whether or not to sort on update events
	 */
	void setSortsOnUpdates(boolean sortsOnUpdates);

	/**
	 * Returns true if a sort should happen when the underlying model is
	 * updated; otherwise, returns false.
	 *
	 * @return whether or not to sort when the model is updated
	 */
	boolean getSortsOnUpdates();

	/**
	 * Sets the StringValueProvider to look up the StringValues. If the value is
	 * not-null, it guarantees to use it exclusively for string conversion.
	 * <p>
	 * 
	 * PENDING JW: this is more or less parallel to TableStringConverter. Need
	 * to think about merging somehow.
	 * 
	 * @param provider
	 *            the look up for StringValues, may be null.
	 */
	void setStringValueProvider(StringValueProvider provider);

	/**
	 * Returns the StringValueProvider used to look up StringValues.
	 * 
	 * @return StringValueProvider used to look up StringValues, guaranteed to
	 *         be not null.
	 */
	StringValueProvider getStringValueProvider();

	// ------------------------ sort

	/**
	 * Reverses the sort order of the specified column. The exact behaviour is
	 * up to implementations.
	 * <p>
	 * 
	 * Implementations must respect the per-controller and per-column-sortable
	 * property.
	 * 
	 * @param column
	 *            the model index of the column to toggle
	 * @see #isSortable(int)
	 * @see #isSortable()
	 */
	void toggleSortOrder(int column);

	/**
	 * Sets the sort order of the specified column.
	 * <p>
	 * 
	 * Implementations must respect the per-controller and per-column-sortable
	 * property.
	 *
	 * @param column
	 *            the model index of the column to set
	 * @param sortOrder
	 *            the SortOrder to set for the column
	 * 
	 * @see #isSortable(int)
	 * @see #isSortable()
	 */
	void setSortOrder(int column, SortOrder sortOrder);

	/**
	 * Returns the sort order of the specified column.
	 * 
	 * 
	 * @return one of {@link SortOrder#ASCENDING}, {@link SortOrder#DESCENDING}
	 *         or {@link SortOrder#UNSORTED}.
	 */
	SortOrder getSortOrder(int column);

	/**
	 * Resets all interactive sorting.
	 * <p>
	 * 
	 * Implementations must respect the per-controller and per-column-sortable
	 * property.
	 * 
	 */
	void resetSortOrders();

	// -------------------- filter

	/**
	 * Sets the filter that determines which rows, if any, should be hidden from
	 * the view. The filter is applied before sorting. A value of
	 * <code>null</code> indicates all values from the model should be included.
	 * <p>
	 * <code>RowFilter</code>'s <code>include</code> method is passed an
	 * <code>Entry</code> that wraps the underlying model. The number of columns
	 * in the <code>Entry</code> corresponds to the number of columns in the
	 * underlying model. The identifier comes from the underlying model as well.
	 * <p>
	 * This method triggers a sort.
	 *
	 * PENDING JW: the "underlying model" is the ModelWrapper ... want to expose
	 * here as well? Otherwise, the second paramter doesn't make much sense.
	 * 
	 * @param filter
	 *            the filter used to determine what entries should be included
	 */
	void setRowFilter(RowFilter<? super M, ? super Integer> filter);

	/**
	 * Returns the filter that determines which rows, if any, should be hidden
	 * from view.
	 *
	 * @return the filter
	 */
	RowFilter<? super M, ? super Integer> getRowFilter();

}